home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Devices and Hardware / SCSI / SCSI Inquiry (More) / SCSI Inquiry(Simple) / DoSCSICommand.c next >
Encoding:
C/C++ Source or Header  |  2000-09-28  |  8.0 KB  |  233 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        DoSCSICommand.c
  3.  
  4.     Contains:    his is a simple sample for the original SCSI manager that shows how to
  5.                 arbitrate for the SCSI bus and send a command using the original SCSI manager.
  6.                 This function is incomplete in that it does not support virtual memory and
  7.                  presumes a six-byte SCSI command block.
  8.      
  9.                  This sample is further limited in that it always does polled reads.
  10.     
  11.                 Calling Sequence:
  12.                 OSErr                DoSCSICommand(
  13.                     unsigned short            targetID,
  14.                     const Ptr                scsiCommand,
  15.                     SCSIInstr                requestTIB
  16.                 );
  17.                 The parameters have the following meaning:
  18.     
  19.                 targetID            The SCSI Bus ID of the target (0 .. 6). Note that this
  20.                                     function can only access LUN zero.
  21.                 scsiCommand            The SCSI Command Block (6 bytes).
  22.                 requestTIB            The transfer request block.
  23.     
  24.                  Return codes:
  25.                 noErr            normal
  26.                 scCommErr        no such device (selection error)
  27.                 scPhaseErr        user data buffer was the wrong size for the transfer.
  28.                                 You may merely have given a large buffer size to
  29.                                 a variable-length request, such as Device Inquiry.
  30.                 sc...            other SCSI error.
  31.                 statusErr        Device returned "Check condition." The caller should
  32.                                 issue a Request Sense SCSI Command to this device.
  33.                 controlErr        Device returned "Busy"
  34.                 ioErr            Other (serious) device status.
  35.  
  36.     Written by: Martin Minow    
  37.  
  38.     Copyright:    Copyright © 1992-1999 by Apple Computer, Inc., All Rights Reserved.
  39.  
  40.                 You may incorporate this Apple sample source code into your program(s) without
  41.                 restriction. This Apple sample source code has been provided "AS IS" and the
  42.                 responsibility for its operation is yours. You are not permitted to redistribute
  43.                 this Apple sample source code as "Apple sample source code" after having made
  44.                 changes. If you're going to re-distribute the source, we require that you make
  45.                 it clear in the source that the code was descended from Apple sample source
  46.                 code, but that you've made changes.
  47.  
  48.     Change History (most recent first):
  49.                 7/15/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  50.                 
  51.  
  52. */
  53. #include <scsi.h>
  54. #include <Errors.h>
  55. #include <OSUtils.h>
  56. #include <Events.h>
  57.  
  58. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  59.  * SCSI command status (from status phase)
  60.  */
  61. #define     kScsiStatusGood            0x00            /* Normal completion        */
  62. #define     kScsiStatusCheckCondition    0x02            /* Need GetExtendedStatus    */
  63. #define     kScsiStatusConditionMet    0x04            /* For Compare Command?        */
  64. #define     kScsiStatusBusy            0x08            /* Device busy (self-test?)    */
  65. #define     kScsiStatusIntermediate    0x10            /* Intermediate status        */
  66. #define     kScsiStatusResConflict        0x18            /* Reservation conflict        */
  67. #define     kScsiStatusQueueFull        0x28            /* Target can't do command    */
  68. #define     kScsiStatusReservedMask    0x3e            /* Vendor specific?            */
  69.  
  70. /*
  71.  * This is the maximum number of times we try to grab the SCSI Bus
  72.  */
  73. #define kMaxSCSIRetries                40                /* 10 seconds, 4 times/sec    */
  74. /*
  75.  * This test is TRUE if the SCSI bus status indicates "busy" (which is the case
  76.  * if either the BSY or SEL bit is set).
  77.  */
  78. #ifndef kScsiStatBSY
  79. #define kScsiStatBSY                (1 << 6)
  80. #endif
  81. #ifndef kScsiStatSEL
  82. #define kScsiStatSEL                (1 << 1)
  83. #endif
  84. #define ScsiBusBusy()        ((SCSIStat() & (kScsiStatBSY | kScsiStatSEL)) != 0)
  85.  
  86.  
  87. OSErr                        DoSCSICommand(
  88.         unsigned short            targetID,
  89.         const Ptr                scsiCommand,
  90.         Ptr                        requestTIB
  91.     );
  92.  
  93. OSErr                        DoSCSICommand(
  94.         unsigned short            targetID,
  95.         const Ptr                scsiCommand,
  96.         Ptr                        requestTIB
  97.     )
  98. {
  99.  
  100.         OSErr                    status;                /* Final status                */
  101.         OSErr                    completionStatus;    /* Status from ScsiComplete    */
  102.         short                    totalTries;            /* Get/Select retries        */
  103.         short                    getTries;            /* Get retries                */
  104.         short                    iCount;                /* Bus free counter            */
  105.         unsigned long            watchdog;            /* Timeout after this        */
  106.         short                    stsByte;            /* Status byte from device    */
  107.         short                    msgByte;            /* Message byte from device    */
  108.         
  109.         status = noErr;
  110.         stsByte = msgByte = (-1);                    /* Preset "bad" values        */
  111.         /*
  112.          * Arbitrate for the scsi bus.  This will fail if some other device is
  113.          * accessing the bus at this time (which is unlikely).
  114.          *
  115.          *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
  116.          *** Do not set breakpoints or call any functions that may require   ***
  117.          *** I/O (such as display code that accesses font resources between  ***
  118.          *** SCSIGet and SCSIComplete,                                         ***
  119.          *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
  120.          */
  121.         for (totalTries = 0; totalTries < kMaxSCSIRetries; totalTries++) {
  122.             for (getTries = 0; getTries < 4; getTries++) {
  123.                 /*
  124.                  * Wait for the bus to go free.
  125.                  */
  126.                 watchdog = TickCount() + 300;        /* 5 second timeout            */
  127.                 while (ScsiBusBusy()) {
  128.                     if (TickCount() > watchdog) {
  129.                         status = scArbNBErr;        /* The bus is busy            */
  130.                         goto exit;                    /* Fail                        */
  131.                     }
  132.                 }
  133.                 /*
  134.                  * The bus is free, try to grab it
  135.                  */
  136.                 for (iCount = 0; iCount < 4; iCount++) {
  137.                     if ((status = SCSIGet()) == noErr)
  138.                         break;
  139.                 }
  140.                 if (status == noErr)                /* If we get the bus, leave    */
  141.                     break;                            /* the "getTries" loop         */
  142.                 /*
  143.                  * The bus became busy again. Try to wait for it to go free.
  144.                  */
  145.                 for (iCount = 0; iCount < 100 && ScsiBusBusy(); iCount++)
  146.                     ;
  147.             } /* The getTries loop */
  148.             if (status != noErr) {
  149.                 /*
  150.                  * If the above loop fails, it means that the SCSI Manager
  151.                  * thinks the bus is not busy and not selected, but "someone" has
  152.                  * set the internal semaphore that signals that the SCSI Manager
  153.                  * itself is busy. The application will have to handle this
  154.                  * problem. (We tried getTries * 4 times).
  155.                  */
  156.                 goto exit;
  157.             }
  158.             /*
  159.              * We now own the SCSI bus. Try to select the device.
  160.              */
  161.             if ((status = SCSISelect(targetID)) != noErr)
  162.                 goto exit;
  163.             /*
  164.              * From this point on, we must exit through SCSIComplete() even if an
  165.              * error is detected. Send a command to the selected device. There are
  166.              * several failure modes, including an illegal command (such as a
  167.              * write to a read-only device). If the command failed because of
  168.              * "device busy", we will try it again.
  169.              */
  170.             status = SCSICmd((Ptr) scsiCommand, 6);
  171.             if (status == noErr)
  172.                 status = SCSIRead((Ptr) requestTIB);
  173. finish:
  174.             /*
  175.              * SCSIComplete "runs" the bus-phase algorithm until the bitter end,
  176.              * returning the status and command-completion message bytes..
  177.              */
  178.             completionStatus = SCSIComplete(
  179.                         &stsByte,
  180.                         &msgByte,
  181.                         (5 * 60)                    /* Five second timeout        */
  182.                     );
  183.             /*
  184.              * If we have an error here, return as the "final" status.
  185.              * 
  186.              */
  187.             if (completionStatus != noErr)
  188.                 status = completionStatus;
  189.             else {
  190.                 /*
  191.                  * ScsiComplete is happy. If the device is busy, Pause for 1/4
  192.                  * second and try again.
  193.                  */
  194.                 if (stsByte == kScsiStatusBusy) {
  195.                     Delay(15, &watchdog);
  196.                     continue;                /* Do next totalTries attempt        */
  197.                 }
  198.             }
  199.             /*
  200.              * This is the normal exit (success) or final failure exit.
  201.              */
  202.             break;
  203.         } /* totalTries loop */
  204. exit:
  205.         /*
  206.          * Return an artificial error if the device returns a non-zero status:
  207.          *    statusErr        Caller should issue RequestSense.
  208.          *    controlErr        Device is busy (self-test?) try again later.
  209.          *    ioErr            Something is dreadfully wrong.
  210.          *    scPhaseErr        If the device returned good status, the user buffer
  211.          *                    was larger than was needed. (This will be the case
  212.          *                    for an inquiry or request sense command.) Since the
  213.          *                    caller can determine the actual length by examining
  214.          *                    the TIB or the buffer contents, we can return noErr.
  215.          * Also, there is a bug in the combination of System 7.0.1 and the 53C96
  216.          * that may cause the real SCSI Status Byte to be in the Message byte.
  217.          */
  218.         if (stsByte == kScsiStatusGood
  219.          && msgByte == kScsiStatusCheckCondition)
  220.             stsByte = kScsiStatusCheckCondition;
  221.         if (status == scPhaseErr && stsByte == kScsiStatusGood)
  222.             status = noErr;
  223.         if (status == noErr) {
  224.             switch (stsByte) {
  225.             case kScsiStatusGood:                                    break;
  226.             case kScsiStatusCheckCondition:    status = statusErr;        break;
  227.             case kScsiStatusBusy:            status = controlErr;    break;
  228.             default:                        status = ioErr;            break;
  229.             }
  230.         }
  231.         return (status);
  232. }
  233.